package com.scvtc.config.security.filter;

import com.scvtc.domain.ResResult;
import com.scvtc.domain.dto.common.SystemLoginUser;
import com.scvtc.exception.TokenTimeOutException;
import com.scvtc.utils.JwtUtil;
import com.scvtc.utils.RedisUtil;
import com.scvtc.utils.WebUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import static com.scvtc.constants.RedisConstant.USER_LOGIN;
import static com.scvtc.domain.enums.AppHttpCodeEnum.FAIL_AUTHENTICATION;
import static com.scvtc.domain.enums.AppHttpCodeEnum.OPERATE_TIME_OUT;

/**
 * @Description: Jwt验证
 * @Author: yiFei
 * @date: 2022/4/13 19:37
 */
@Component
public class JwtTokenFilter extends OncePerRequestFilter {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private JwtUtil jwtUtil;

    @Value("${jwt.token-header}")
    private String tokenHeader;

    @Override
    protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
        //获取请求头中的token
        String token = request.getHeader(tokenHeader);
        // 判断头是否为空
        if (StringUtils.hasText(token)) {
            String userId = null;
            try {
                userId = jwtUtil.getTokenInfo(tokenHeader, token, String.class);
                SystemLoginUser loginUser = redisUtil.getCacheObject(USER_LOGIN.concat(userId));
                // 是否可以从 redis 中查询到数据
                if (!ObjectUtils.isEmpty(loginUser)) {
                    // 如果获取到 就加长时间
                    redisUtil.expire(USER_LOGIN.concat(userId), 30, TimeUnit.MINUTES);
                    /**
                     * 使用 3 个参数的 UsernamePasswordAuthenticationToken
                     * 在 3 个参数的 UsernamePasswordAuthenticationToken 中 会调用 setAuthenticated(true) 标记登录认证
                     * 然后 UsernamePasswordAuthenticationToken 中的 principal 会进入线程 让经过过滤的全部接口都有这个 loginUser
                     * 当然 你可以 new 全空得的 UsernamePasswordAuthenticationToken 只是不能使用 authorities 和 principal
                     */
                    SecurityContextHolder.getContext().setAuthentication(
                            new UsernamePasswordAuthenticationToken(
                                    loginUser, null, loginUser.getAuthorities()
                            )
                    );
                } else {
                    throw new TokenTimeOutException(OPERATE_TIME_OUT.getMsg());
                }
            } catch (Exception e) {
                // redis中获取不到 或者 jwt 解析不了
                if (StringUtils.hasText(userId)) {
                    WebUtil.resultJson(response, ResResult.customize(OPERATE_TIME_OUT));
                } else {
                    WebUtil.resultJson(response, ResResult.customize(FAIL_AUTHENTICATION));
                }

            }
        }
        filterChain.doFilter(request, response);
    }
}
